package com.example.basic.web.service.menu.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.example.basic.base.impl.BaseEntityServiceImpl;
import com.example.basic.exception.ServiceException;
import com.example.basic.utils.*;
import com.example.basic.web.enums.DataCodeEnum;
import com.example.basic.web.enums.EnableTypeEnum;
import com.example.basic.web.enums.YesNoEnum;
import com.example.basic.web.mapper.menu.MenuMapper;
import com.example.basic.web.model.menu.Menu;
import com.example.basic.web.model.role.Role;
import com.example.basic.web.service.role.RoleService;
import com.example.basic.web.service.dataCode.DataCodeService;
import com.example.basic.web.service.menu.MenuService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.example.basic.utils.CheckUtil.notNull;


@Service
public class MenuServiceImpl extends BaseEntityServiceImpl<MenuMapper, Menu> implements MenuService {

    @Resource
    private MenuMapper menuMapper;

    @Resource
    private DataCodeService dataCodeService;

    @Resource
    private RoleService roleService;


    /**
     * 保存或者修改
     * @param menu 模型
     * @return
     */
    @Override
    public Menu addOrUpdate(Menu menu) {
        //参数检验
        notNull(menu.getMenuName(),"菜单名称不能为空");
        notNull(menu.getParentMenuCode(),"父级菜单编码不能为空");
        //校验名称是否重复
        LambdaQueryWrapper<Menu> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Menu::getMenuName,menu.getMenuName());
        if (menu.getId() != null){
            notNull(menu.getMenuCode(),"菜单编码不能为空");
            lqw.ne(Menu::getMenuCode,menu.getMenuCode());
        }
        Menu detail = menuMapper.selectOne(lqw);
        if(detail != null){
            throw new ServiceException("当前菜单名称已存在");
        }

        //如果新增菜单不为父级菜单，则校验父级菜单是否为虚拟菜单
        if (!YesNoEnum.NO_FLAG.getType().equals(menu.getParentMenuCode())) {
            Menu parentMenu = detail(menu.getParentMenuCode());
            if (YesNoEnum.NO_FLAG.getType().equals(parentMenu.getAddChild())) {
                throw new ServiceException("父级菜单必须为虚拟菜单");
            }
            menu.setParentMenuName(parentMenu.getMenuName());
        }

        //修改
        if (menu.getId() != null){
            preUpdate(menu);
        }
        //新增
        else {
            //生成菜单编码
            String menuCode = dataCodeService.generateDataCodeNo(DataCodeEnum.MENU_CODE.getType());
            menu.setMenuCode(menuCode);
            preCreate(menu);
        }

        //生成拼音码五笔码
        generatePyWbCode(menu);

        this.save(menu);
        return detail(menu.getMenuCode());
    }

    /**
     * @create 生成五笔码、拼音码
     * @param menu 系统用户模型实体类
     */
    private void generatePyWbCode(Menu menu) {
        // 获取菜单名
        String menuName = StringUtils.safeToString(menu.getMenuName(), "");
        // 生成拼音码
        if("".equals(StringUtils.safeToString(menu.getPyCode(),"").replaceAll(" ",""))) {
            String userNamePinyin = PinyinUtils.getSimplePinYin(menuName);
            menu.setPyCode(userNamePinyin);
        }
        // 生成拼音码
        if("".equals(StringUtils.safeToString(menu.getWbCode(),"").replaceAll(" ",""))) {
            String userNameWubi = WubiUtils.getSimpleWuBi(menuName);
            menu.setWbCode(userNameWubi);
        }
    }


    /**
     * @create 获取子级菜单
     * @param userSysMenuList 用户所拥有的的系统菜单列表
     * @param parentSysMenu 父级菜单
     * @return
     */
    public List<Map<String, Object>> getChildMenus(List<Menu> userSysMenuList, Menu parentSysMenu){
        try {
            // 定义最终返回的list
            List<Map<String, Object>> childMenuList = new ArrayList<Map<String, Object>>();

            // list快速查询工具类
            FSearchUtils fSearchUtils = new FSearchUtils(userSysMenuList, false, "parentMenuCode");
            // 搜索的关键字，要去掉首尾的空格
            List childrenList = fSearchUtils.searchTasks(parentSysMenu.getMenuCode());

            //对childrenList使用manuCode进行排序
            childrenList.sort((o1, o2) -> {
                Menu menu1 = (Menu) o1;
                Menu menu2 = (Menu) o2;
                return menu1.getMenuCode().compareTo(menu2.getMenuCode());
            });

            // 如果查询出来的子集不为空，则对子集进行赋值、递归查找子集的子集
            if (childrenList != null && !childrenList.isEmpty()) {

                long startTime1 = System.currentTimeMillis();
                //查询父级菜单
                childrenList.forEach(childMenu -> {
                    // 对对象强转进行赋值
                    Menu sysMenu = (Menu) childMenu;
                    // 如果父节点不为空，则在此记录上设置父节点名称
                    if(parentSysMenu != null){
                        sysMenu.setParentMenuName(parentSysMenu.getMenuName());
                    }
                    // 转换并设置子信息
                    Map<String, Object> map = ObjectUtils.toMap(sysMenu);
                    map.put("childMenuList", getChildMenus(userSysMenuList, (Menu) childMenu));
                    childMenuList.add(map);
                });
            }
            return childMenuList;
        } catch (Exception e){
            throw new ServiceException("查找子菜单发生错误");
        }
    }

    /**
     * 详情
     * @param menuCode 菜单编码
     * @return
     */
    @Override
    public Menu detail(String menuCode) {
        notNull(menuCode, "菜单编码不能为空");

        LambdaQueryWrapper<Menu> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Menu::getMenuCode,menuCode);
        return  this.mapper.selectOne(lqw);
    }

    /**
     * 删除
     * @param menuCode 菜单编码
     * @return
     */
    @Override
    public int delete(String menuCode) {
        notNull(menuCode,"菜单编码不能为空");

        Menu menu = detail(menuCode);
        if(menu == null){
            throw new ServiceException("未查询到当前数据");
        }

        //校验是否存在子级菜单
        LambdaQueryWrapper<Menu> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Menu::getParentMenuCode,menuCode);
        List<Menu> list = menuMapper.selectList(lqw);

        if (list != null && list.size() > 0){
            throw new ServiceException("当前菜单存在子级菜单,不允许删除");
        }

        return menuMapper.deleteById(menu.getId());
    }

    /**
     * 启用/禁用
     * @param menuCode 菜单编码
     * @return
     */
    @Override
    public int enableOrDisable(String menuCode) {
        notNull(menuCode,"菜单编码不能为空");

        Menu menu = detail(menuCode);

        if(menu == null){
            throw new ServiceException("未查询到当前数据");
        }

        //停用启用
        if(menu.getEnableFlag().equals(EnableTypeEnum.ON.getType())){
            menu.setEnableFlag(EnableTypeEnum.OFF.getType());
        }else {
            menu.setEnableFlag(EnableTypeEnum.ON.getType());
        }

        preUpdate(menu);

        return menuMapper.updateById(menu);
    }

    /**
     * 根据角色编码查询所拥有的菜单
     *
     * @param roleCode 角色编码
     * @param sysType
     * @return
     */
    @Override
    public List<Map<String, Object>> queryListByRoleCode(String roleCode, String sysType) {
        List<Menu> menuList = new ArrayList<>();
        if(StringUtils.emptyParams(roleCode)){
            menuList = menuMapper.queryList(null,"0",sysType);
        }else {
            //验证权限信息
            Role role = roleService.detail(roleCode);
            if(role == null) {
                throw new ServiceException("权限不可用，请联系管理员");
            }

            //此处查询的是菜单下的功能,但是只要菜单信息,所以SQL中进行去重
            menuList = menuMapper.queryListByRoleCode(roleCode,sysType);
        }
        // 定义list，用来存放最终的结果
        List<Map<String, Object>> menuMapList = new ArrayList<Map<String, Object>>();

        List<Menu> parentSysMenuList =menuList.stream().filter(s -> "0".equals(s.getParentMenuCode())).collect(Collectors.toList());

        //排序
        parentSysMenuList.sort((o1, o2) -> {
            Menu menu1 = (Menu) o1;
            Menu menu2 = (Menu) o2;
            return menu1.getMenuCode().compareTo(menu2.getMenuCode());
        });

        // 先拿到最顶层的菜单
        List<Menu> finalUserSysMenuList = menuList;

        parentSysMenuList.forEach(parentSysMenu -> {
            parentSysMenu.setParentMenuName(parentSysMenu.getMenuName());
            // 此处定义Map,主要考虑到不知道菜单具体有几层
            Map<String, Object> menuMap = ObjectUtils.toMap(parentSysMenu);

            // 此方法作用主要是为了处理此节点下的菜单层级，并返回此菜单下的所有菜单层级关系
            List<Map<String, Object>> childMenuList = getChildMenus(finalUserSysMenuList, parentSysMenu);
            // 如果子菜单列表不为空，则进行添加操作
            if(childMenuList != null && childMenuList.size() > 0){

                // 设置此菜单的子集
                menuMap.put("childMenuList",childMenuList);

                // @modify zhangy_ruiyi 没有子集的顶级菜单也一起返回
                // 往系统用户的菜单列表中添加数据
                menuMapList.add(menuMap);
            }

        });
        return menuMapList;
    }

    /**
     * 菜单列表
     *
     * @param searchParam
     * @param isAll
     * @param menuCode
     * @param sysType
     * @return
     */
    @Override
    public List<Menu> queryMenuListAndParentMenu(String searchParam, String isAll, String menuCode, String sysType) {
        // 查询数据列表
        List<Menu> menuList = menuMapper.queryMenuListAndParentMenu(searchParam,isAll,menuCode,sysType);

        // 判断查询的数据集合是否为空
        if (menuList == null || menuList.size() == 0) {
            return menuList;
        }
        // 设置菜单的上下级并返回数据
        return this.setLeaderMember(menuList).stream().distinct().collect(Collectors.toList());
    }



    /**
     * @author 设置菜单上下级数据信息 zhangy_ruiyi 2020-11-17
     * @param menuList 菜单列表
     * @return
     */
    private List<Menu> setLeaderMember(List<Menu> menuList) {

        // 判断查询的数据集合是否为空
        if (menuList == null || menuList.size() == 0) {
            return menuList;
        }

        // 取出所有上级数据
        List<Menu> leaderMenuList = new ArrayList<>();

        // 取出所有下级数据
        List<Menu> memberMenuList = new ArrayList<>();

        // 集合不为空
        for (Menu menu : menuList) {

            // 判断是否为上级数据
            if ("0".equals(menu.getParentMenuCode())) {

                // 上级菜单代码为 0 为顶级数据
                leaderMenuList.add(menu);
            } else {

                // 不是顶级数据则放入下级数据集合
                memberMenuList.add(menu);
            }
        }

        // 判断上级数据集合是否为空
        if (leaderMenuList != null && leaderMenuList.size() > 0) {

            // 判断下级数据集合是否为空
            if (memberMenuList == null || memberMenuList.size() == 0) {

                // 下级数据集合为空直接返回上级数据
                return leaderMenuList;
            }

            // 设置菜单下级数据
            this.setMenu(leaderMenuList, memberMenuList);

            // 再次判断下级集合是否为空
            if (memberMenuList == null || memberMenuList.size() == 0) {

                // 下级数据为空时直接返回上级信息
                return leaderMenuList;
            }

            // 获取剩余菜单的上级数据
            List<Menu> leaderMenu = this.queryLeaderDept(memberMenuList);

            // 设置菜单下级数据
            this.setMenu(leaderMenu, memberMenuList);

            // 清空原来的数据
            menuList.clear();

            // 将所有的数据都添加到需要返回的集合中
            menuList.addAll(leaderMenuList);
            menuList.addAll(leaderMenu);


            // 返回设置的数据
            return menuList;
        }

        // 上级信息为空时判断下级数据是否为空
        if (memberMenuList == null || memberMenuList.size() == 0) {

            // 下级数据为空时直接返回
            return menuList;
        }

        // 获取剩余菜单的上级数据
        List<Menu> leaderMenu = this.queryLeaderDept(memberMenuList);

        // 设置菜单下级数据
        this.setMenu(leaderMenu, memberMenuList);

        // 数据设置完成直接返回
        return leaderMenu;
    }

    /**
     * @author 获取菜单上级信息 zhangy_ruiyi 2020-11-17
     * @param memberMenuList 下级菜单集合
     * @return
     */
    private List<Menu> queryLeaderDept(List<Menu> memberMenuList) {

        // 设置父级数据
        List<Menu> menuArrayList = new ArrayList<>();

        // 下级信息不为空时，根据下级数据集合查询上级数据
        memberMenuList.stream().map(menu ->

                // 根据子集数据查询父级数据
                this.detail(menu.getParentMenuCode())).forEach(menu -> {

            // 设置父级信息
            menuArrayList.add(menu);
        });

        // 返回上级信息，多个子集可能有同一个父级，去重后进行返回
        return menuArrayList.stream().distinct().collect(Collectors.toList());
    }

    /**
     * @author 设置菜单下级信息 zhangy_ruiyi 2020-11-17
     * @param leaderMenuList 上级菜单数据集合
     * @param memberMenuList 下级菜单数据集合
     */
    private void setMenu(List<Menu> leaderMenuList, List<Menu> memberMenuList) {

        // 下级数据也不为空则设置数据
        for (Menu menu : leaderMenuList) {

            // 判断菜单实体是否为空
            if (menu == null) {
                continue;
            }

            // 创建一个存放子集的集合
            List<Menu> list = new ArrayList<>();

            // 遍历上级集合和下级集合判断子集数据
            for (int i = 0; i < memberMenuList.size(); i++) {

                // 判断菜单实体是否为空
                if (memberMenuList.get(i) == null) {
                    continue;
                }

                // 判断是否为是改数据的子集
                if (menu.getMenuCode().equals(memberMenuList.get(i).getParentMenuCode())) {

                    // 将数据设置到子集中
                    list.add(memberMenuList.get(i));

                    // 将改数据删除
                    memberMenuList.remove(memberMenuList.get(i));

                    // 下标减一防止下标异常
                    i--;
                }
            }

            if(list.size() > 0 && memberMenuList.size() > 0){
                this.setMenu(list,memberMenuList);
            }

            // 获取到子集信息并设置到对应的父级中
            menu.setChildList(list);
        }
    }

}
